using System;
using System.Collections.Generic;
using FireStarterModeller.BO;
using Habanero.Base;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.DB;

namespace FireStarterModeller.Test.BO
{
// ReSharper disable RedundantNameQualifier
    public static class TestUtilsDomain
    {
        /// <summary>
        /// Waits for the garbage collector to clear dereferenced objects in order
        /// to ensure accurate testing
        /// </summary>
        public static void WaitForGC()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public static void SetupTest()
        {
            WaitForGC();
            ClearAllTables();
            BusinessObjectManager.Instance.ClearLoadedObjects();
        }

        public static void SetupFixture()
        {
            ClassDef.ClassDefs.Clear();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(BOBroker.GetClassDefsXml(), new DtdLoader()).LoadClassDefs());
            BORegistry.DataAccessor = new DataAccessorInMemory();
        }

        #region DataGenerators

//        private static readonly Random rnd = new Random();

//        public static int GetRandomInt(int minNo, int maxNo)
//        {
//            return rnd.Next(minNo, maxNo);
//        }
//
//        public static string GetRandomString(int minNo, int maxNo)
//        {
//            return rnd.Next(minNo, maxNo).ToString();
//        }
//
//        public static string GetRandomString()
//        {
//            return rnd.Next().ToString();
//        }

        #endregion

        /// <summary>
        /// Clears all the database tables holding business object data.
        /// WARNING: This is an irreversible action, be sure to use a test database.
        /// </summary>
        public static void ClearAllTables()
        {
            if (BORegistry.DataAccessor is DataAccessorInMemory)
            {
                BORegistry.DataAccessor = new DataAccessorInMemory();
            }
            else
            {
                //-----------------------------------------------------------------------
                // Deletes the contents of each of the tables.
                // Orders the tables to prevent parents being deleted before their
                // children, by assuming that the object on the 1 side of 1-to-many
                // is the parent.
                // Replace these lines below if this does not work for your setup.
                // Table names may need delimiters - adjust as needed for your database.
                //-----------------------------------------------------------------------
                foreach (ClassDef classDef in GetClassDefsOrderedForDeletion())
                {
                    DatabaseConnection.CurrentConnection.ExecuteRawSql("Delete from " + classDef.TableName);
                }
            }
        }

        /// <summary>
        /// Gets a list of classdefs ordered for deletion in order to avoid
        /// foreign key conflicts where parents are deleted before their children.
        /// </summary>
        public static IList<ClassDef> GetClassDefsOrderedForDeletion()
        {
            IList<ClassDef> classDefs = new List<ClassDef>();
            foreach (ClassDef classDef in ClassDef.ClassDefs)
            {
                classDefs.Add(classDef);
            }

            bool deleteOrderCorrect;
            do
            {
                deleteOrderCorrect = true;
                for (int position = 0; position < classDefs.Count; position++)
                {
                    ClassDef classDef = classDefs[position];
                    for (int parentPosition = 0; parentPosition < position; parentPosition++)
                    {
                        ClassDef potentialParentClassDef = classDefs[parentPosition];
                        if (ClassMustBeDeletedBeforeParent(classDef, potentialParentClassDef))
                        {
                            classDefs.Remove(classDef);
                            classDefs.Insert(parentPosition, classDef);
                            deleteOrderCorrect = false;
                            break;
                        }
                    }
                    if (!deleteOrderCorrect) break;
                }
            } while (!deleteOrderCorrect);

            return classDefs;
        }

        /// <summary>
        /// Indicates if the given class needs to be deleted before the one provided,
        /// which occurs when the parent is on the 1 side of 1-to-many
        /// </summary>
        private static bool ClassMustBeDeletedBeforeParent(IClassDef childClassDef, ClassDef potentialParentClassDef)
        {
            foreach (IRelationshipDef relationshipDef in potentialParentClassDef.RelationshipDefCol)
            {
                if (relationshipDef.RelatedObjectClassName + relationshipDef.RelatedObjectAssemblyName
                    == childClassDef.ClassName + childClassDef.AssemblyName
                    && relationshipDef is MultipleRelationshipDef)
                {
                    return true;
                }
            }
            return false;
        }
    }

    // ReSharper restore RedundantNameQualifier
}